﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SudokuGame.Sudoku
{
    /// <summary>
    /// Plocha hry Sudoku
    /// </summary>
    public class SudokuSheet : IEnumerable<SudokuCell>
    {
        #region Konstrukce
        /// <summary>
        /// Vytvoří a vrátí novou hru.
        /// Lze zadat jakékoli číslo, vybere se odpovídající hra.
        /// Fyzicky hra obsahuje několik předzadaných her (nemá vlastní generátor nových), počet her = statická property GamePresetCount.
        /// Lze tedy zadat hodnotu 0 až (GamePresetCount - 1), při zadání jiné hodnoty se reálné číslo hry určí jako (zadané číslo % GamePresetCount).
        /// </summary>
        /// <param name="number">Číslo hry</param>
        /// <returns></returns>
        public static SudokuSheet CreateSheet(int number)
        {
            SudokuSheet sudokuSheet = new SudokuSheet();
            sudokuSheet._FillFrom(_GetGameByNumber(number));
            return sudokuSheet;
        }
        /// <summary>
        /// Vytvoří a vrátí novou hru, na základě dodaného textu.
        /// Text může mít tvar Serial (ze zdejšího objektu), 
        /// nebo tvar 9x9 řádků (textová klasika), 
        /// nebo text zkopírovaný z adresy http://sudokuzdarma.cz/
        /// Pokud text není rozpoznán, bude vráceno null.
        /// </summary>
        /// <param name="text">Text</param>
        /// <returns></returns>
        public static SudokuSheet CreateSheet(string text)
        {
            SudokuSheet sudokuSheet = new SudokuSheet();
            bool detected = sudokuSheet._FillFrom(text);
            if (!detected)
                return null;
            return sudokuSheet;
        }
        private SudokuSheet()
        {
            this.Initialize();
        }
        private SudokuSheet(string serial)
        {
            this.Initialize();
            this.Serial = serial;
        }
        private SudokuSheet(SudokuCell[,] cells)
        {
            this.Initialize();
            if (cells != null && cells.GetLength(0) == 9 && cells.GetLength(1) == 9)
            {
                this._Cells = cells;
                this._FixState();
            }
        }
        private void _FixState()
        {
            this.ForEach((r, c) => this._Cells[r, c].FixState());
        }
        /// <summary>
        /// Provede danou akci pro všechny souřadnice našich buněk
        /// </summary>
        /// <param name="action"></param>
        private void ForEach(Action<int, int> action)
        {
            for (int r = 0; r < 9; r++)
                for (int c = 0; c < 9; c++)
                    action(r, c);
        }
        /// <summary>
        /// Provede danou akci pro každou buňku
        /// </summary>
        private void ForEach(Action<SudokuCell> action)
        {
            for (int r = 0; r < 9; r++)
                for (int c = 0; c < 9; c++)
                    action(this._Cells[r, c]);
        }
        private SudokuCell[,] _Cells;
        public override string ToString()
        {
            return this.Text;
        }
        #endregion
        #region Property
        /// <summary>
        /// Hodnota v dané buňce
        /// </summary>
        /// <param name="row">Řádek 0 - 8</param>
        /// <param name="column">Sloupec 0 - 8</param>
        /// <returns></returns>
        public Int32? this[int row, int column]
        {
            get { return this._Cells[row, column].Value; }
            set
            {
                if (this._Cells[row, column].IsFixed)
                    throw new InvalidOperationException("Attempt to write to fixed cell [" + row.ToString() + "," + column.ToString() + "]");
                this._Cells[row, column].Value = value;
                this._ResetState();
            }
        }
        /// <summary>
        /// Jednoduché textové zobrazení
        /// </summary>
        public string Text
        {
            get
            {
                string result = "";
                this.ForEach((r, c) =>
                    {
                        if ((c > 0) && (c % 3) == 0) result += " ";
                        SudokuCell cell = this._Cells[r, c];
                        result += (cell.HasValue ? cell.Text : ".");
                        if (c == 8 && r < 8) result += Environment.NewLine;
                    });
                return result;
            }
        }
        /// <summary>
        /// Buňky SUDOKU
        /// </summary>
        public SudokuCell[,] Cells { get { return this._Cells; } }
        /// <summary>
        /// Izolovaný klon this
        /// </summary>
        public SudokuSheet Clone
        {
            get
            {
                string serial = this.Serial;
                SudokuSheet clone = new SudokuSheet(this.Serial);
                clone.Serial = serial;
                return clone;
            }
        }
        /// <summary>
        /// Textový obsah this. Lze zálohovat i obnovovat stav.
        /// </summary>
        public string Serial
        {
            get
            {
                string serial = "";
                ForEach(cell => serial += cell.Serial);
                return serial;
            }
            set
            {
                string serial = value;
                if (serial != null && serial.Length == 2 * 9 * 9)
                {
                    this.ForEach(cell =>
                        {
                            cell.Serial = serial.Substring(0, 2);
                            serial = serial.Substring(2);
                        });
                    this._ResetState();
                }
            }
        }
        #endregion
        #region Public metody
        /// <summary>
        /// Smaže vše, data budou komplet prázdná
        /// </summary>
        public void Initialize()
        {
            this._Cells = new SudokuCell[9, 9];
            this.ForEach((r, c) => this._Cells[r, c] = new SudokuCell(r, c));
        }
        /// <summary>
        /// Smaže řešení, data budou obsahovat jen Fixed hodnoty
        /// </summary>
        public void Clear()
        {
            this.ForEach((r, c) => this._Cells[r, c].Clear());
        }
        #endregion
        #region Kontrola dat
        public void DetectState()
        {
            this._DetectState();
        }
        /// <summary>
        /// Stav hry
        /// </summary>
        public SudokuState State
        {
            get 
            {
                if (!this._State.HasValue)
                    this._DetectState();
                return this._State.Value;
            }
        }
        private void _ResetState()
        {
            this._State = null;
        }
        private void _DetectState()
        {
            this._State = SudokuState.Empty;
            this._Modes = new _DetectMode[] { _DetectMode.Rows, _DetectMode.Columns, _DetectMode.Group };

            bool hasFixed = false;
            bool hasNonFixed = false;
            bool hasError = false;
            int valueCount = 0;

            this.ForEach((r, c) => this._DetectState(r, c, ref hasFixed, ref hasNonFixed, ref hasError, ref valueCount));

            // Souhrnný stav:
            SudokuState state = SudokuState.None;
            if (valueCount == 0)
            { state = SudokuState.Empty; }
            else if (hasError)
            { state = SudokuState.HasError; }
            else if (valueCount == (9*9))
            { state = SudokuState.Solved; }
            else if (!hasNonFixed)
            { state = SudokuState.Initiated; }
            else
            { state = SudokuState.NowSolving; }

            this._State = state;
        }
        /// <summary>
        /// Detekuje stav jedné buňky, současně s tím pomáhá detekovat stav celé hry
        /// </summary>
        /// <param name="r"></param>
        /// <param name="c"></param>
        /// <param name="hasFixed"></param>
        /// <param name="hasNonFixed"></param>
        /// <param name="hasError"></param>
        /// <param name="valueCount"></param>
        private void _DetectState(int r, int c, ref bool hasFixed, ref bool hasNonFixed, ref bool hasError, ref int valueCount)
        {
            SudokuCell cell = this._Cells[r, c];
            cell.HasError = false;
            if (!cell.HasValue) return;

            valueCount++;
            if (!hasFixed && cell.IsFixed)
                hasFixed = true;
            else if (!hasNonFixed && !cell.IsFixed)
                hasNonFixed = true;

            foreach (_DetectMode mode in _Modes)
            {
                SudokuCell[] otherCells = _GetOtherCells(r, c, mode);
                foreach (SudokuCell otherCell in otherCells)
                {
                    if (otherCell.HasValue && otherCell.Number == cell.Number)
                    {   // Konfliktní hodnota:
                        if (!cell.IsFixed || (cell.IsFixed && otherCell.IsFixed))
                        {   // Pokud aktivní buňka není fixovaná, pak je chybná. 
                            // Pokud aktivní buňka je fixovaná, pak je chybná jen tehdy, když je v konfliktu s jinou fixovanou buňkou (=chybné zadání):
                            cell.HasError = true;
                            if (!hasError)
                                hasError = true;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Vrátí sousední buňky pro vyhodnocení, v daném režimu, kromě dané buňky (tj. vrací 8 buněk)
        /// </summary>
        /// <param name="r"></param>
        /// <param name="c"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        private SudokuCell[] _GetOtherCells(int r, int c, _DetectMode mode)
        {
            switch (mode)
            {
                case _DetectMode.Rows: return _GetOtherCellsRows(r, c);
                case _DetectMode.Columns: return _GetOtherCellsColumns(r, c);
                case _DetectMode.Group: return _GetOtherCellsGroups(r, c);
            }
            return null;
        }
        /// <summary>
        /// Vrátí sousední buňky pro vyhodnocení - všechny buňky v daném řádku, kromě buňky z daného sloupce
        /// </summary>
        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <returns></returns>
        private SudokuCell[] _GetOtherCellsRows(int row, int col)
        {
            SudokuCell[] cells = new SudokuCell[8];
            int i = 0;
            for (int c = 0; c < 9; c++)
                if (c != col)
                    cells[i++] = this._Cells[row, c];
            return cells;
        }
        /// <summary>
        /// Vrátí sousední buňky pro vyhodnocení - všechny buňky v daném sloupci, kromě buňky z daného řádku
        /// </summary>
        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <returns></returns>
        private SudokuCell[] _GetOtherCellsColumns(int row, int col)
        {
            SudokuCell[] cells = new SudokuCell[8];
            int i = 0;
            for (int r = 0; r < 9; r++)
                if (r != row)
                    cells[i++] = this._Cells[r, col];
            return cells;
        }
        /// <summary>
        /// Vrátí sousední buňky pro vyhodnocení - všechny buňky v daném čtverci, kromě buňky z dané souřadnice
        /// </summary>
        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <returns></returns>
        private SudokuCell[] _GetOtherCellsGroups(int row, int col)
        {
            SudokuCell[] cells = new SudokuCell[8];
            int i = 0;
            int r1 = (row / 3) * 3;
            int r2 = r1 + 3;
            int c1 = (col / 3) * 3;
            int c2 = c1 + 3;
            for (int r = r1; r < r2; r++)
                for (int c = c1; c < c2; c++)
                    if (r != row || c != col)
                        cells[i++] = this._Cells[r, c];
            return cells;
        }
        /// <summary>
        /// Režim kontroly
        /// </summary>
        private enum _DetectMode { None, Rows, Columns, Group }
        private _DetectMode[] _Modes;
        private SudokuState? _State;
        #endregion
        #region Knihovna přednastavených her
        /// <summary>
        /// Počet přednastavených her
        /// </summary>
        public static int GamePresetCount { get { return PresetGames.Length; } }
        private static string _GetGameByNumber(int number)
        {
            // http://sudokuzdarma.cz/
            string[] games = PresetGames;
            int idx = number % games.Length;
            return games[idx];
        }
        /// <summary>
        /// Přednastavené hry
        /// </summary>
        private static string[] PresetGames
        {
            get
            {
                string[] games = new string[] {
                    ".-5f.-.-.-.-.-.-.-4f.-8f1f.-9f.-.-.-7f9f.-.-.-2f.-5f.-8f.-.-.-.-.-.-.-9f.-.-5f.-.-.-.-7f.-6f4f.-.-.-.-.-.-.-.-8f.-.-.-4f.-.-5f.-.-.-5f9f.-.-1f2f.-.-1f6f.-8f.-4f7f",
                    ".-4f3f.-.-2f9f.-.-.-8f1f.-6f7f.-.-3f.-6f.-.-3f.-4f.-.-.-.-.-.-.-.-.-1f9f6f.-4f8f1f.-2f.-.-.-.-.-.-.-.-7f.-.-.-.-.-.-.-.-.-9f.-.-.-.-.-.-.-.-.-7f.-1f2f.-.-.-6f.-.-",
                    ".-.-.-.-.-.-.-.-.-7f1f.-.-.-8f.-.-.-.-4f.-.-.-7f9f.-8f.-9f2f.-.-.-.-.-.-6f.-7f.-.-2f1f4f.-4f.-.-6f.-.-2f9f.-1f.-.-8f.-5f.-2f.-.-6f4f.-2f1f8f.-.-.-.-.-4f.-.-.-.-7f",
                    "5f.-.-.-9f.-.-3f.-.-4f8f1f.-.-.-5f.-9f.-.-2f5f.-7f.-.-.-.-2f5f.-.-.-.-.-.-5f.-.-1f.-4f8f.-.-9f7f4f.-.-.-.-1f.-.-.-.-.-.-.-2f.-.-.-.-.-.-.-3f.-.-.-8f.-.-.-.-.-1f.-",
                    "1f.-4f.-2f.-.-.-8f.-.-3f6f8f.-2f.-.-5f.-.-1f.-.-9f.-.-2f8f.-.-5f.-.-.-.-.-.-.-4f.-.-1f.-.-.-.-6f.-9f.-.-.-.-9f4f1f.-.-2f.-5f.-.-.-.-.-.-.-.-.-.-.-.-.-9f6f.-.-.-.-",
                    "3f.-.-.-.-.-.-.-.-1f5f4f.-.-.-.-.-.-.-2f.-.-6f4f.-.-.-.-.-.-.-4f.-3f.-.-.-3f.-8f.-1f.-2f.-5f.-.-9f.-.-.-.-6f6f.-.-.-.-.-.-.-.-.-.-2f1f5f.-.-8f.-9f.-.-4f.-2f.-1f.-",
                    ".-.-.-.-.-.-.-.-.-.-.-.-.-9f.-6f4f5f.-.-.-.-6f7f.-2f.-2f.-.-.-.-8f.-.-.-.-.-.-.-.-.-.-9f.-8f.-.-.-1f.-.-.-.-.-.-6f.-.-2f.-.-4f7f.-9f8f.-.-.-1f.-.-8f.-.-.-4f5f7f.-",
                    "5f.-.-.-3f2f1f4f.-3f4f.-.-.-.-6f.-.-.-1f6f.-.-5f.-2f.-7f.-.-.-2f1f.-.-.-.-.-.-3f.-8f.-.-.-.-.-.-.-7f.-5f8f.-.-.-.-.-.-.-4f3f8f.-.-.-.-.-.-.-7f.-.-.-1f.-8f.-2f.-.-",
                    ".-.-4f3f.-.-.-.-.-.-.-.-.-.-.-1f.-6f.-.-.-.-9f8f5f.-.-.-.-7f.-3f.-.-.-.-.-.-8f.-.-.-.-.-.-9f1f.-7f.-.-4f8f.-.-.-.-6f.-4f.-.-7f.-.-.-1f7f.-9f3f.-.-.-.-.-.-.-8f.-5f",
                    ".-4f3f.-.-.-.-.-6f.-9f.-.-.-1f5f2f.-.-.-6f5f4f.-.-8f.-.-.-.-.-.-2f.-4f.-.-8f.-.-.-.-.-.-.-.-.-.-6f.-.-.-.-9f.-.-8f.-5f.-.-.-.-6f.-.-.-.-.-.-1f.-.-1f4f.-.-.-6f9f.-",

                    ".-5f6f.-4f.-7f.-.-9f.-.-.-.-.-.-2f.-2f4f.-.-5f7f.-3f.-.-9f.-6f.-.-.-.-.-5f.-.-.-.-.-.-.-.-7f.-.-.-.-.-.-.-2f.-.-.-3f.-5f.-8f.-.-.-.-.-.-8f.-9f6f4f.-.-7f2f.-.-.-.-",
                    ".-4f1f9f.-3f.-7f.-.-3f.-.-6f4f.-.-5f.-5f.-8f1f.-6f.-.-.-7f.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-4f.-.-.-.-.-5f8f.-.-.-.-.-.-.-.-.-.-.-9f.-8f.-3f.-.-.-.-7f6f.-.-4f.-5f9f.-",
                    ".-.-.-5f1f7f3f.-.-.-1f.-.-6f3f.-.-.-.-.-.-2f.-.-.-.-4f.-5f9f.-.-.-.-7f.-.-6f2f.-.-.-.-.-.-.-.-.-.-.-.-2f.-3f.-.-7f.-9f6f.-5f.-.-.-.-1f.-.-.-.-.-.-.-.-3f4f5f1f9f.-",
                    ".-7f9f.-1f8f.-.-.-2f5f.-.-.-6f.-.-.-8f.-4f2f.-.-.-.-.-7f.-1f8f6f.-.-.-.-.-9f.-.-.-5f2f.-.-4f.-.-.-.-9f.-.-.-.-.-6f.-.-2f.-5f.-5f4f2f.-.-.-.-.-.-.-.-.-.-4f.-.-.-8f",
                    ".-.-.-6f.-.-5f.-.-9f7f.-.-.-.-4f.-.-.-8f6f7f.-1f.-.-9f.-6f.-.-.-4f.-2f.-.-.-.-.-.-8f.-7f6f.-.-.-.-.-2f1f.-.-.-.-1f.-.-.-8f.-2f2f9f.-.-1f.-.-.-5f.-.-.-.-.-.-7f.-.-",
                    ".-.-.-4f.-.-5f.-8f5f.-.-8f.-1f6f2f4f.-.-.-.-.-.-.-.-.-3f.-.-.-.-.-1f.-.-6f.-.-.-.-.-2f8f9f.-.-.-.-.-.-.-.-6f4f.-.-6f.-8f.-5f.-1f3f2f.-4f.-.-.-.-.-.-.-.-.-3f4f.-1f",
                    ".-.-.-6f8f7f.-9f.-.-.-.-.-.-.-.-.-7f.-.-.-.-3f.-6f.-.-3f.-.-.-2f.-8f.-.-8f.-.-7f.-.-.-.-.-.-.-.-4f9f.-.-.-.-6f7f.-.-.-.-.-5f2f4f8f.-3f.-2f.-7f.-.-.-.-.-.-.-9f8f.-",
                    ".-.-.-.-2f.-4f7f.-.-.-.-.-.-9f.-.-2f.-.-.-.-.-.-.-.-.-2f.-9f3f.-.-5f.-.-.-.-5f9f.-.-1f.-7f1f.-.-.-.-7f8f.-.-.-.-.-.-.-8f.-.-1f.-.-.-.-4f.-.-.-8f8f.-.-1f7f2f.-4f.-",
                    "4f5f.-.-.-.-1f8f.-.-6f.-1f4f5f.-.-.-.-.-.-.-.-.-.-.-.-7f4f.-9f.-.-5f1f.-3f.-.-.-.-4f8f.-.-6f.-.-7f1f.-.-3f9f8f3f.-.-.-.-.-.-.-.-.-.-5f9f.-.-.-.-.-.-.-8f.-3f.-.-.-",
                    ".-.-.-2f7f.-.-8f.-.-8f9f.-.-.-.-.-.-.-.-.-.-.-5f.-4f.-.-.-.-6f9f2f.-.-.-.-.-2f4f.-.-8f.-.-.-6f.-5f.-3f.-.-1f9f5f.-.-.-.-.-.-.-3f.-.-.-.-.-.-1f4f.-.-1f.-.-4f5f.-.-",
                    
                    ".-.-4f1f.-2f.-.-.-.-7f.-.-5f9f.-.-.-.-.-.-.-.-.-.-6f9f.-8f.-.-.-.-5f.-.-.-9f5f4f.-1f.-.-.-1f.-.-.-.-.-.-8f6f.-.-.-.-9f6f.-4f7f.-.-.-5f.-.-.-.-.-.-.-.-.-2f7f.-.-5f",
                    ".-.-.-.-.-7f.-.-.-.-.-.-.-.-.-.-.-1f.-5f.-9f6f3f.-.-.-.-.-.-.-3f5f.-.-.-3f1f.-.-4f.-7f.-.-.-.-.-.-.-.-.-9f.-2f6f.-.-7f4f1f.-.-1f3f.-2f.-.-4f.-.-7f.-9f.-.-.-2f5f.-",
                    "1f.-2f.-.-7f.-6f.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-4f2f.-9f5f.-7f.-.-.-.-.-1f9f2f.-.-.-5f.-.-.-.-.-6f.-.-.-.-2f3f.-9f4f.-.-.-.-.-.-.-.-.-.-.-.-3f.-7f2f5f.-9f.-6f.-.-.-",
                    ".-.-8f.-3f.-.-.-.-.-.-.-.-.-5f8f4f.-.-.-7f.-.-.-.-6f5f.-.-.-.-.-.-.-.-.-5f.-.-.-.-.-.-7f4f.-7f2f4f.-.-3f.-9f.-6f.-.-2f.-.-.-.-7f8f.-.-.-.-4f2f.-.-4f.-5f8f.-6f3f.-",
                    ".-.-.-.-.-5f2f7f.-.-.-.-1f2f.-.-.-9f1f8f.-.-.-3f6f.-.-.-2f7f.-.-6f.-.-.-8f1f.-5f.-.-.-.-.-9f6f.-.-.-.-3f8f2f.-.-.-.-3f8f.-.-.-.-.-.-2f5f.-.-.-.-.-.-.-.-.-.-.-1f.-",
                    ".-.-4f2f.-1f.-7f.-2f.-.-.-5f7f9f.-.-.-3f.-.-9f.-.-1f8f.-.-.-.-.-.-.-.-.-.-9f.-.-.-.-.-.-.-.-.-.-3f1f.-.-8f.-.-.-9f.-8f.-.-4f5f8f7f.-.-.-2f.-.-9f4f.-.-.-.-3f.-.-.-",
                    ".-4f1f.-.-.-.-2f.-6f2f3f.-.-.-.-.-.-9f.-.-.-.-.-.-.-.-1f.-4f.-.-2f6f.-.-3f6f2f.-1f.-8f.-.-.-.-.-.-.-.-.-.-7f.-.-7f.-2f6f.-.-8f4f.-.-7f9f.-.-.-.-.-.-.-.-.-8f1f.-9f",
                    ".-.-.-3f8f.-4f.-.-.-.-.-.-.-.-.-.-.-.-.-4f1f9f.-.-.-5f.-.-.-4f.-.-3f5f.-5f.-.-.-3f9f2f.-1f6f.-1f.-.-.-.-.-9f.-.-.-5f4f.-8f.-3f.-.-.-8f2f1f.-6f.-.-.-.-.-.-.-1f.-.-",
                    ".-.-.-1f.-.-.-.-.-6f.-.-2f3f.-.-.-1f.-.-2f7f4f.-8f.-5f.-.-.-.-.-7f.-8f3f.-8f.-.-6f1f.-7f.-.-.-.-.-.-.-6f.-.-.-.-.-.-8f.-.-.-.-.-5f4f.-.-.-2f.-.-.-.-.-6f.-.-3f1f.-",
                    ".-.-.-.-.-5f9f.-.-3f.-.-.-1f2f8f.-.-.-.-.-6f3f.-.-.-5f.-.-6f.-2f.-1f.-.-.-.-.-.-.-.-.-.-9f.-.-1f3f.-.-.-.-.-.-6f.-.-.-.-2f.-4f2f.-.-9f.-4f.-8f.-.-3f.-.-.-8f5f.-6f",
                    
                    "5f.-.-8f.-.-.-.-7f3f.-.-.-9f7f.-2f5f.-1f.-.-2f.-8f.-.-.-.-.-.-.-1f.-.-.-.-.-.-.-.-.-7f.-.-.-.-1f.-4f.-.-.-.-.-.-.-.-.-5f4f.-8f8f5f.-.-.-9f.-.-3f9f2f7f.-.-.-1f.-.-",
                    ".-.-6f.-.-.-.-.-.-.-.-.-.-5f.-.-8f.-4f.-9f.-6f.-.-.-3f6f8f4f.-.-.-.-5f.-.-2f.-.-.-.-.-.-.-.-.-.-.-.-7f6f2f.-.-.-8f.-9f6f2f.-4f.-.-.-.-.-.-.-.-.-2f.-3f.-.-.-.-9f8f",
                    ".-.-.-1f.-.-.-.-.-.-.-1f3f.-.-.-.-7f.-.-4f.-.-7f6f.-8f.-.-.-.-3f.-.-.-9f9f.-7f4f.-1f.-3f.-.-5f6f.-.-.-8f.-.-.-.-.-.-.-.-.-6f.-.-1f9f6f.-.-.-8f3f.-.-5f.-.-.-.-.-4f",
                    ".-2f.-4f7f5f.-.-.-.-.-.-.-.-.-7f.-.-4f.-9f.-.-1f.-.-.-.-.-.-7f3f8f.-5f.-.-5f.-.-.-.-8f7f.-.-.-.-.-2f.-.-1f.-.-.-.-.-8f3f5f.-.-9f.-3f.-.-.-2f.-4f.-.-.-1f.-.-.-.-.-",
                    ".-5f.-8f1f.-.-.-6f.-3f.-7f.-.-.-.-5f.-.-.-.-.-4f.-.-.-.-.-.-.-.-.-4f2f8f.-.-.-6f.-.-1f5f.-.-.-.-.-.-7f.-.-.-5f.-3f.-4f.-2f7f.-4f.-.-.-6f.-.-.-.-.-.-2f.-.-.-8f.-.-",
                    ".-.-.-.-.-.-.-.-3f9f.-.-3f.-.-.-.-.-7f.-.-.-8f.-.-.-.-.-.-3f.-5f.-.-8f.-.-.-.-9f1f.-5f3f.-.-4f.-.-.-.-.-.-7f.-7f.-.-.-.-.-.-2f.-.-.-1f9f.-4f.-.-.-2f4f.-3f.-1f5f.-",
                    ".-9f.-.-5f.-.-.-2f.-.-.-.-.-.-.-.-9f.-.-.-.-.-.-.-.-.-3f.-.-.-6f.-.-5f8f.-.-8f1f.-.-4f.-.-.-6f5f8f9f.-.-2f.-.-.-1f3f.-.-.-.-5f.-8f.-.-2f.-.-6f.-.-.-.-.-.-6f9f.-3f",
                    ".-.-.-2f.-.-.-6f.-.-5f.-.-.-.-.-.-.-.-.-9f.-7f8f.-.-2f.-.-8f9f2f.-.-1f.-.-.-.-.-1f7f3f2f.-.-.-.-.-.-5f.-7f.-.-.-1f.-3f2f.-.-5f9f8f2f.-.-.-.-.-.-.-3f.-.-.-.-.-.-.-",
                    ".-5f.-.-.-.-.-7f6f.-9f7f5f.-.-.-.-.-3f4f.-.-.-.-2f.-.-.-.-.-3f.-.-7f.-.-4f.-.-.-2f.-1f.-.-1f.-3f.-.-9f.-6f.-.-.-6f.-.-.-.-9f5f.-.-.-.-.-.-.-.-.-.-3f.-.-.-.-4f.-2f",
                    "1f.-.-.-4f.-.-.-.-.-.-8f.-.-9f.-.-4f.-.-.-.-.-.-5f.-.-.-1f.-.-3f8f4f5f.-.-.-.-5f2f.-1f.-8f4f.-.-9f.-.-6f.-.-.-4f.-.-.-.-2f3f.-.-.-3f.-1f.-.-.-5f.-.-.-.-8f.-.-6f.-",
                    
                    ".-.-.-.-.-.-3f5f.-.-.-4f2f.-.-.-.-6f.-.-.-3f5f.-8f.-7f.-.-.-6f.-.-.-.-9f.-.-.-.-.-.-.-.-.-.-.-7f4f.-5f.-3f2f4f.-8f.-3f.-9f.-5f.-.-.-5f6f.-.-.-.-.-6f.-.-.-9f.-7f8f",
                    "6f.-.-.-.-9f8f.-.-.-.-3f.-4f5f.-.-2f.-.-.-3f.-8f1f.-.-.-.-.-1f.-.-4f3f5f1f5f.-.-.-.-.-2f.-.-.-.-.-.-.-6f.-.-9f.-8f.-.-.-.-.-.-.-6f.-8f.-.-.-.-.-.-3f2f6f.-.-.-.-1f",
                    ".-.-.-.-.-.-4f2f.-2f.-1f.-.-.-9f.-.-.-.-.-.-.-.-.-.-.-9f.-3f.-.-4f.-1f.-.-7f.-2f.-.-.-.-.-6f.-4f.-.-9f7f.-.-4f.-.-1f.-7f.-3f2f7f.-.-4f.-2f8f.-.-.-6f.-.-.-.-.-7f.-",
                    "4f8f1f7f.-.-3f.-2f.-.-.-.-.-.-.-.-.-.-.-7f.-8f1f5f.-4f.-7f.-.-.-5f.-.-3f3f.-.-.-2f.-.-.-.-.-9f5f8f.-7f2f.-.-.-.-.-.-9f.-.-.-.-2f4f8f5f.-.-.-.-.-.-5f.-.-.-.-.-.-8f",
                    ".-.-8f.-.-.-7f.-.-3f.-.-.-.-.-.-.-8f1f.-5f.-7f.-.-9f6f6f.-.-.-.-.-.-.-1f.-.-.-.-8f3f.-.-.-.-.-.-9f.-1f2f.-.-7f2f.-.-.-.-.-.-9f.-9f.-.-.-.-.-8f.-.-.-6f2f9f.-.-.-.-",
                    ".-.-.-7f.-4f8f.-.-2f.-.-.-9f.-.-.-.-.-7f6f8f.-1f9f.-.-.-.-.-.-.-.-.-.-7f8f2f.-1f.-.-.-4f.-.-.-.-.-.-6f.-8f.-6f9f7f.-.-.-.-2f.-1f.-.-.-.-.-.-7f5f.-.-4f.-.-.-.-.-1f",
                    ".-.-.-.-.-.-.-7f.-.-7f.-5f.-.-3f.-8f.-.-4f.-3f.-.-.-.-.-.-.-.-.-.-.-.-6f5f.-.-.-6f.-8f3f.-.-9f.-2f4f.-.-5f.-.-5f.-9f.-.-7f8f.-.-.-.-.-.-.-.-.-2f6f.-9f.-.-.-.-.-.-",
                    ".-.-.-.-.-.-.-.-.-7f.-5f.-.-.-.-1f6f8f.-4f.-.-.-.-.-.-.-.-.-.-2f.-.-7f.-1f.-3f.-6f.-.-.-8f6f.-2f.-4f.-.-3f.-5f.-6f2f7f.-.-.-.-2f.-.-.-.-8f.-.-.-.-8f7f3f1f.-.-2f5f",
                    "5f.-.-4f2f.-.-.-.-.-.-4f6f.-.-.-.-1f9f.-.-.-.-.-.-.-8f.-.-.-.-1f.-2f.-.-.-.-1f.-.-.-.-.-.-8f5f.-.-.-.-9f.-.-.-.-.-.-.-3f.-.-4f1f3f.-5f.-.-.-9f.-.-.-2f8f.-.-3f.-.-",
                    ".-.-.-3f.-8f.-4f.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-1f3f7f4f.-2f.-.-.-3f.-.-.-7f1f6f.-.-.-.-.-5f.-3f1f.-.-8f2f.-.-2f.-.-7f.-.-.-.-.-.-5f4f.-1f.-.-.-.-.-.-.-.-2f.-5f4f",
                    
                    ".-.-.-.-.-9f.-2f5f2f.-.-8f.-.-1f3f.-.-5f.-.-.-.-.-.-.-9f7f.-.-.-.-.-1f.-.-.-.-.-.-.-.-.-2f8f.-3f.-.-.-7f.-9f6f.-.-.-8f.-.-.-.-.-9f.-.-.-1f.-.-.-.-3f.-7f2f.-.-5f6f",
                    ".-9f.-.-.-8f.-.-.-7f.-.-.-.-.-.-.-8f.-3f.-.-.-.-7f6f.-.-.-5f.-.-.-6f3f2f6f.-.-.-.-.-9f.-.-.-.-.-.-8f.-.-.-5f.-.-8f3f5f.-.-9f.-2f5f.-.-7f.-.-.-1f.-.-.-.-2f9f.-.-.-",
                    ".-2f.-.-5f.-7f.-1f.-.-5f.-.-.-9f.-3f.-.-.-1f.-4f.-.-.-5f.-.-4f3f.-1f6f7f.-.-.-.-.-.-.-.-.-6f.-3f.-.-.-5f.-.-3f5f.-7f.-.-6f.-.-7f.-4f.-.-2f.-.-.-.-.-9f.-.-.-.-.-4f",
                    ".-4f.-.-5f.-.-.-7f.-.-.-.-.-.-1f3f.-.-6f3f.-.-.-4f5f.-.-.-.-.-.-.-.-.-.-.-.-7f.-.-.-3f4f.-4f.-5f.-1f.-7f.-.-.-.-.-5f.-.-.-1f.-2f.-.-.-9f.-6f.-.-.-.-9f7f.-3f.-.-2f",
                    "1f.-.-2f.-.-3f.-5f.-2f.-.-.-.-.-.-6f.-8f7f.-9f.-.-.-1f2f.-.-9f.-.-.-5f.-.-.-.-.-.-.-8f.-.-.-.-.-.-8f3f.-.-.-3f.-5f8f.-2f.-9f.-.-.-.-.-.-.-6f.-.-.-.-.-1f.-.-.-.-2f",
                    ".-1f.-.-5f.-7f.-.-.-.-.-1f.-8f.-6f5f.-.-7f.-.-.-9f.-.-.-8f.-.-.-.-.-.-.-.-2f5f.-1f.-6f.-.-.-.-.-.-.-.-2f8f1f2f.-.-5f8f.-.-3f.-5f.-.-9f.-3f.-.-7f7f.-.-.-6f.-.-.-.-",
                    "5f2f7f.-6f.-4f.-.-.-.-.-.-.-1f5f.-.-.-8f6f.-.-.-.-2f.-.-.-5f3f.-.-7f.-1f.-.-.-.-5f6f8f.-.-.-.-2f7f.-.-.-.-.-7f.-.-6f2f.-.-.-8f.-.-.-.-4f3f.-.-.-.-3f.-.-.-.-.-.-.-",
                    "5f.-1f.-.-.-2f.-.-.-.-.-.-.-.-.-.-.-9f.-.-.-.-.-.-.-.-6f.-.-.-.-1f9f.-2f.-.-5f.-6f8f.-1f.-.-.-.-3f9f.-.-.-6f7f.-9f.-8f6f1f.-.-.-2f.-.-.-5f6f8f.-.-.-.-.-.-2f7f.-9f",
                    ".-.-.-7f.-.-.-.-.-.-6f.-1f.-.-3f.-2f.-.-.-.-.-.-.-.-.-.-2f.-.-.-.-.-1f9f9f.-.-.-6f1f.-.-.-.-.-7f3f9f.-6f.-.-3f7f.-6f.-5f.-.-1f2f.-9f.-3f.-.-5f.-.-.-.-9f.-.-.-.-8f",
                    "2f.-.-.-.-.-3f.-5f.-.-7f4f.-1f.-.-2f.-3f.-.-.-.-.-.-.-4f.-.-.-.-7f.-.-.-.-.-.-8f.-.-.-.-.-.-8f1f5f.-.-.-.-9f9f7f8f.-.-.-.-4f.-.-.-2f7f.-4f9f.-.-.-.-.-.-.-2f1f3f.-",

                    // Brutální:
                    ".-8f.-.-9f.-5f.-.-.-.-.-.-.-.-9f.-.-7f.-.-.-3f.-.-4f.-.-1f.-.-.-.-.-5f7f.-.-.-.-.-9f.-.-.-.-.-2f.-.-1f4f9f.-3f.-.-.-.-.-.-1f.-.-5f4f3f7f.-.-.-.-2f9f.-.-.-.-7f.-5f",
                    ".-.-5f7f9f.-2f.-.-.-2f.-.-.-4f.-.-.-.-1f.-.-.-.-.-4f3f.-.-.-.-1f.-.-9f.-.-.-1f6f7f.-.-.-4f.-6f.-.-.-5f3f.-.-.-.-.-.-.-.-.-2f.-.-.-4f.-.-.-9f.-.-.-.-9f.-.-.-7f.-6f"
                };
                return games;
            }
        }
        private bool _FillFrom(string game)
        {
            this.Initialize();

            if (game == null) return false;
            string serial = null;
            if (game.Length == 9 * 9 * 2)
                serial = game;
            else if (game.ToCharArray().Count(c => c == '\r') >= 8)
                serial = _GetSerialFromLines(game.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));

            if (String.IsNullOrEmpty(serial))
                return false;
            if (serial.Length != (9*9*2))
                return false;
            this.Serial = serial;
            return true;
        }
        private string _GetSerialFromLines(string[] lines)
        {
            if (lines.Length >= 27 && lines.Count(l => l.Count(c => c == '\t') == 2) >= 27)
                return _GetSerialFromLines339(lines);

            if (lines.Length >= 9 && lines.Min(l => l.Length) >= 9)
                return _GetSerialFromLines99(lines);

            return null;
        }
        /// <summary>
        /// Vrátí serial z formátu 3*3*9
        /// </summary>
        /// <param name="lines"></param>
        /// <returns></returns>
        private string _GetSerialFromLines339(string[] lines)
        {
            string serial = "";

            int fl = 0;
            int fg = 0;
            string[] frags = new string[3];
            foreach (string l in lines)
            {
                int len = l.Length;
                if (l.Count(c => c == '\t') != 2 || len > 5) continue;

                string fragment = _GetFragment339(l);

                if (fl == 0 && fg == 0)
                {
                    frags[0] = "";
                    frags[1] = "";
                    frags[2] = "";
                }

                frags[fl] = frags[fl] + fragment;
                fl++;
                if (fl == 3)
                {
                    fl = 0;
                    fg++;
                    if (fg == 3)
                    {
                        serial += frags[0] + frags[1] + frags[2];
                        if (serial.Length == (9 * 9 * 2)) break;
                        fg = 0;
                    }
                }
               
            }
            return serial;
        }
        private string _GetFragment339(string line)
        {
            int length = line.Length;
            if (length == 2) return "......";
            if (length == 3)
            {
                if (line[1] == '\t' && line[2] == '\t') return line[0].ToString() + "f....";                             // 1--    =   1..
                if (line[0] == '\t' && line[2] == '\t') return ".." + line[1].ToString() + "f..";                        // -2-    =   .2.
                if (line[0] == '\t' && line[1] == '\t') return "...." + line[2].ToString() + "f";                        // --3    =   ..3
                return "......";
            }
            if (length == 4)
            {
                if (line[1] == '\t' && line[3] == '\t') return line[0].ToString() + "f" + line[2].ToString() + "f..";         // 1-2-   =   12.
                if (line[0] == '\t' && line[2] == '\t') return ".." + line[1].ToString() + "f" + line[3].ToString() + "f";    // -2-3   =   .23
                if (line[1] == '\t' && line[2] == '\t') return line[0].ToString() + "f.." + line[3].ToString() + "f";         // 1--3   =   1.3
                return "......";
            }
            return line[0].ToString() + "f" + line[2].ToString() + "f" + line[4].ToString() + "f";         // 1-2-3   =   123
        }
        /// <summary>
        /// Vrátí serial z formátu 9*9
        /// </summary>
        /// <param name="lines"></param>
        /// <returns></returns>
        private string _GetSerialFromLines99(string[] lines)
        {
            string serial = "";
            int row = 0;
            string values = "0123456789";
            foreach (string line in lines)
            {
                if (String.IsNullOrEmpty(line)) continue;

                int column = 0;
                foreach (char c in line)
                {
                    int index = values.IndexOf(c);
                    Int32? value = (index <= 0 ? (Int32?)null : (Int32?)index);
                    serial += (value.HasValue ? (value.Value.ToString() + "f") : "..");
                    column++;
                    if (column > 8) break;
                }
                serial += "".PadRight(((9 - column) * 2), '.');
                row++;
                if (row > 8) break;
            }
            serial += "".PadRight(((9 - row) * 18), '.');
            return serial;
        }
        #endregion
        #region IEnumerable<SudokuCell> Members
        IEnumerator<SudokuCell> IEnumerable<SudokuCell>.GetEnumerator()
        {
            List<SudokuCell> cells = new List<SudokuCell>();
            this.ForEach(cell => cells.Add(cell));
            return cells.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._Cells.GetEnumerator();
        }
        #endregion
    }
    #region class SudokuCell : Jedna buňka hry - její obsah, režim, stav.
    /// <summary>
    /// Jedna buňka hry - její obsah, režim, stav.
    /// </summary>
    public class SudokuCell
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        public SudokuCell(int row, int column)
        {
            this.Row = row;
            this.Column = column;
        }
        /// <summary>
        /// Číslo řádku [index] této buňky
        /// </summary>
        public int Row { get; private set; }
        /// <summary>
        /// Číslo sloupce [index] této buňky
        /// </summary>
        public int Column { get; private set; }
        /// <summary>
        /// Hodnota nebo nic
        /// </summary>
        public Int32? Value { get; set; }
        /// <summary>
        /// Je fixovaná = jde o zadání hry, ne o řešení.
        /// </summary>
        public bool IsFixed { get; private set; }
        /// <summary>
        /// Obsahuje chybu?
        /// </summary>
        public bool HasError { get; set; }
        /// <summary>
        /// Má vepsanou hodnotu 1 - 9?
        /// </summary>
        public bool HasValue { get { return (this.Value.HasValue && this.Value.Value >= 1 && this.Value.Value <= 9); } }
        /// <summary>
        /// Text pro zobrazení: pokud má hodnotu (HasValue), pak 1 znak "1" - "9", jinak prázdný string
        /// </summary>
        public string Text { get { return (this.HasValue ? this.Value.ToString() : ""); } }
        /// <summary>
        /// Číselná hodnota pro výpočetní funkce.
        /// Pokud buňka nemá hodnotu, je zde 0.
        /// </summary>
        public int Number { get { return (this.HasValue ? this.Value.Value : 0); } }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// Stav (Empty, HasError, Solved (když je IsFixed), NowSolving (má hodnotu a není IsFixed)
        /// </summary>
        public SudokuState State
        {
            get
            {
                if (!this.Value.HasValue) return SudokuState.Empty;
                if (this.HasError) return SudokuState.HasError;
                if (this.IsFixed) return SudokuState.Solved;
                return SudokuState.NowSolving;
            }
        }
        /// <summary>
        /// Stringový obsah (hodnota + fixed)
        /// </summary>
        public string Serial
        {
            get
            {
                return (this.HasValue ? this.Text : ".") + (this.IsFixed ? "f" : "-");
            }
            set
            {
                this._Initialize();
                if (value != null && value.Length == 2)
                {
                    int i = "0123456789".IndexOf(value[0]);
                    this.Value = (i <= 0 ? (Int32?)null : (Int32?)i);
                    this.IsFixed = value[1] == 'f';
                }
            }
        }
        /// <summary>
        /// Smaže obsah i příznaky
        /// </summary>
        private void _Initialize()
        {
            this.Value = (Int32?)null;
            this.IsFixed = false;
            this.HasError = false;
        }
        /// <summary>
        /// Smaže řešení, data budou obsahovat jen Fixed hodnoty
        /// </summary>
        public void Clear()
        {
            if (!this.IsFixed)
                this.Value = null;
            this.HasError = false;
        }
        /// <summary>
        /// Určí svou hodnotu IsFixed podle své aktuální hodnoty
        /// </summary>
        public void FixState()
        {
            this.IsFixed = (this.HasValue);
        }
    }
    #endregion
    /// <summary>
    /// 
    /// </summary>
    public delegate void SudokuProgressHandler(object sender, SudokuProgressArgs args);
    /// <summary>
    /// 
    /// </summary>
    public class SudokuProgressArgs
    {
        public SudokuProgressArgs(SudokuSheet sheet, int nodeCount, int brancheCount)
        {
            this.Sheet = sheet;
            this.NodeCount = nodeCount;
            this.BrancheCount = brancheCount;
        }
        public SudokuSheet Sheet { get; private set; }
        public int NodeCount { get; private set; }
        public int BrancheCount { get; private set; }
    }
    /// <summary>
    /// Stav hry
    /// </summary>
    public enum SudokuState
    {
        None = 0,
        Empty,
        Initiated,
        NowSolving,
        Solved,
        HasError
    }
}
